
# 非负矩阵分解
"""
非负矩阵分解（NMF）是一种无监督学习算法，用于提取有用特征和降维。与 PCA 类似，NMF 试图将数据点表示为分量的加权求和，但不同之处在于：
 1.NMF 要求分量和系数均为非负，因此只能应用于非负特征的数据。
 2.NMF 更适合处理由多个独立源叠加而成的数据（如多人说话或多种乐器的音乐），能够识别出原始分量。
 3.与 PCA 相比，NMF 的结果更容易解释，因为非负分量避免了负值导致的抵消效应。

"""
import mglearn.plots
from matplotlib import pyplot as plt
from sklearn.decomposition import PCA

# 在将 NMF 应用于人脸数据集之前，我们先来简要回顾一下模拟数据。

# 将 NMF 应用于模拟数据
'''
与 PCA 不同，NMF 需要数据是非负的，因此数据相对于原点 (0, 0) 的位置对 NMF 很重要。提取的非负分量可以被视为从原点 (0, 0) 指向数据的方向。
'''
mglearn.plots.plot_nmf_illustration()
# plt.show()

'''
NMF是一种降维技术，它将数据分解为一组非负的基成分和相应的权重：
        
    1.用两个分量做NMF时：所有数据点都可以通过这两个分量的不同组合来重建。就好比你有两个基本的“原料”，通过调整它们的比例，可以还原出所有的数据点。
    2.分量足够多时：NMF会选择数据变化最大的方向作为分量。因为这些方向能捕捉到数据中最显著的变化，从而更好地还原数据。
    3.只有一个分量时：NMF会选择指向数据平均值的方向。因为平均值是对数据整体最好的近似，能最小化还原误差。
    4.与PCA不同：NMF在减少分量时，不仅会去掉一些方向，还会重新生成一组完全不同的分量。这是因为NMF的分量受非负约束的影响，所以它的分量选择方式和PCA不一样。
    5.分量没有固定顺序：在NMF中，所有分量都是平等的，不像PCA那样有“第一主成分”这种按重要性排序的概念。
    6.随机初始化的影响：NMF的计算过程是从随机值开始的，所以不同的随机种子可能会导致不同的结果。在简单的情况下，比如只有两个分量的数据，随机性影响不大，
      因为数据很容易被完美还原。但在复杂的情况下，随机初始化可能会导致很大的差异，因为可能存在多种有效的分解方式。
'''

# 将 NMF 应用于人脸图像
'''
在用 NMF 处理 Wild 数据集的人脸图像时，关键参数是 n_components，也就是要提取的分量个数。这个数字通常要小于输入特征的数量（比如像素数）。
如果分量个数等于或大于特征数，NMF 就会把每个像素当作独立分量，完美重建数据，但这样就失去了降维的意义。
通过选择较少的分量，NMF 可以提取数据中的关键特征，同时降低数据维度。
'''

# 首先，我们来观察分量个数如何影响 NMF 重建数据的好坏
# 运行会话费很多时间
# mglearn.plots.plot_nmf_faces(X_train,X_test,image_shape)

# plt.show()

'''
NMF 的反向变换质量比 PCA 稍差，这是正常的，因为 PCA 是为了重建数据找到最佳方向。
NMF 更多是用于从数据中发现有趣的模式，而不是用于重建或编码数据。
'''

# 用t-SNE进行流形学习
'''
以下是关于流形学习算法、t-SNE 算法、NMF 参数及应用的总结：
流形学习算法
流形学习算法是一种用于降维和可视化的非线性方法，目标是从高维数据中恢复低维的流形结构。
它假设数据在高维空间中分布在一个低维流形上，通过捕捉数据的内在关系和模式，实现降维。
常见的流形学习方法包括：
 等度量映射（Isomap）：通过计算数据点之间的测地距离进行降维。
 局部线性嵌入（LLE）：保持数据局部线性关系，适用于非线性数据。
 拉普拉斯特征映射（Laplacian Eigenmaps）：利用图的拉普拉斯矩阵捕捉局部邻域关系。
流形学习在处理非线性数据结构时特别有效，可以揭示数据背后的复杂关系。

t-SNE 算法
t-SNE（t-Distributed Stochastic Neighbor Embedding）是一种非线性降维算法，特别适用于高维数据的可视化。
其核心思想是将高维空间中的数据点映射到低维空间（通常是二维或三维），同时尽可能保持数据点之间的局部相似性。
t-SNE 的特点包括：
 重点关注距离较近的点，保留点的邻近信息。
 适用于数据可视化和聚类分析。
 计算复杂度较高，不适合大规模数据。
 
NMF 参数及应用
非负矩阵分解（NMF）是一种无监督学习算法，广泛用于数据降维和特征提取。
其关键参数是 n_components，表示要提取的分量个数，通常小于输入特征的数量。
NMF 的应用包括：
 图像处理：提取图像的关键特征，如人脸方向和明暗。
 文本挖掘：识别文档中的主题。
 音频处理：分离音乐中的不同乐器。
NMF 的分量是非负的，因此结果更容易解释。'''

# 我们将对 scikit-learn 包含的一个手写数字数据集 2 应用 t-SNE 流形学习算法。
from sklearn.datasets import load_digits
digits = load_digits()
fig,axes = plt.subplots(2,5,figsize=(10,5),subplot_kw={'xticks': (), 'yticks': ()})
for ax, img in zip(axes.ravel(), digits.images):
 ax.imshow(img)

# plt.show()

# 我们用 PCA 将降到二维的数据可视化。我们对前两个主成分作图，并按类别对数据点着色
# 构建一个PCA模型
pca = PCA(n_components=2)
pca.fit(digits.data)
# 将digits数据变换到前两个主成分的方向上
digits_pca = pca.transform(digits.data)
colors = ["#476A2A", "#7851B8", "#BD3430", "#4A2D4E", "#875525",
 "#A83683", "#4E655E", "#853541", "#3A3120", "#535D8E"]
plt.figure(figsize=(10, 10))
plt.xlim(digits_pca[:, 0].min(), digits_pca[:, 0].max())
plt.ylim(digits_pca[:, 1].min(), digits_pca[:, 1].max())
for i in range(len(digits.data)):
 # 将数据实际绘制成文本，而不是散点
 plt.text(digits_pca[i, 0], digits_pca[i, 1], str(digits.target[i]),
 color = colors[digits.target[i]],
 fontdict={'weight': 'bold', 'size': 9})
plt.xlabel("First principal component")
plt.ylabel("Second principal component")


# 实际上，这里我们用每个类别对应的数字作为符号来显示每个类别的位置。利用前两个主
# 成分可以将数字 0、6 和 4 相对较好地分开，尽管仍有重叠。大部分其他数字都大量重叠在一起。

'''
我们将 t-SNE 应用于同一个数据集，并对结果进行比较。由于 t-SNE 不支持变换新数据，
所以 TSNE 类没有 transform 方法。我们可以调用 fit_transform 方法来代替，它会构建模型并立刻返回变换后的数据
'''
from sklearn.manifold import TSNE
tsne = TSNE(random_state=42)
# 使用fit_transform而不是fit，因为TSNE没有transform方法
digits_tsne = tsne.fit_transform(digits.data)
plt.figure(figsize=(10, 10))
plt.xlim(digits_tsne[:, 0].min(), digits_tsne[:, 0].max() + 1)
plt.ylim(digits_tsne[:, 1].min(), digits_tsne[:, 1].max() + 1)
for i in range(len(digits.data)):
 # 将数据实际绘制成文本，而不是散点
 plt.text(digits_tsne[i, 0], digits_tsne[i, 1], str(digits.target[i]),
 color = colors[digits.target[i]],
 fontdict={'weight': 'bold', 'size': 9})
plt.xlabel("t-SNE feature 0")
plt.xlabel("t-SNE feature 1")
plt.show()

'''

t-SNE 的结果非常出色，能够将不同类别清晰地分开，尽管某些类别（如数字 1 和 9）可能被分成几块，但大多数类别都形成了密集的组。重要的是，t-SNE 是无监督的，它仅根据原始数据点之间的距离关系，就能在二维空间中明确区分各个类别。
此外，t-SNE 有一些可调节的参数（如 perplexity 和 early_exaggeration），但默认参数通常已经表现良好，调整这些参数的作用可能并不显著。
'''